/*
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2006-2008 Intel Corporation. All Rights Reserved.
//
*/
#include "umc_defs.h"
#if defined (UMC_ENABLE_DVHD_VIDEO_ENCODER)

#include "umc_dv100_video_encoder.h"
#include "umc_dv100_enc_1080_60i_segment_reader.h"
#include "umc_dv100_enc_1080_50i_segment_reader.h"
#include "umc_dv100_enc_720_60p_segment_reader.h"
#include "umc_dv100_enc_segment_compressor.h"
#include "umc_dv_enc_segment_writer.h"
#include "umc_video_processing.h"
#include <ipps.h>

namespace UMC
{

enum
{
    DV100_SYSTEM_720_WIDTH                  = 1280,
    DV100_SYSTEM_1080_WIDTH                 = 1920,
    DV100_SYSTEM_720_60P_WIDTH_DOWNSAMPLED  = 960,
    DV100_SYSTEM_720_HEIGHT                 = 720,
    DV100_SYSTEM_1080_60I_WIDTH_DOWNSAMPLED = 1280,
    DV100_SYSTEM_1080_50I_WIDTH_DOWNSAMPLED = 1440,
    DV100_SYSTEM_1080_HEIGHT                = 1080
};

DV100VideoEncoder::DV100VideoEncoder(void) :
    m_DownsampledFrameBufferMID(0),
    m_nSystem720_LastDecodedChannel(0)

{
}

DV100VideoEncoder::~DV100VideoEncoder(void)
{
    Close();
}


Status DV100VideoEncoder::Init(BaseCodecParams* pBaseParams)
{
    VideoEncoderParams *pParams = DynamicCast<VideoEncoderParams> (pBaseParams);
    Ipp32s LumaDataSize;

    if (pParams == NULL)
        return UMC_ERR_NULL_PTR;

    if (m_bIsInit)
        Close();

    //init Huffman Tables
    //377 ENCODE_ELEMENT entries
    //63 Ipp16s elements in TableMaxAmpOnRun table
    //62 (ENCODE_ELEMENT*) entries for encode table
    //4096 - reserved for alignment
    newHuffmanTablesBuffer = ippsMalloc_8u(377 * 4 + 63 * 2 + 62 * 4 + 4096);
    InitHuffmanTables(newHuffmanTablesBuffer);

    if( !( (pParams->info.clip_info.width == DV100_SYSTEM_1080_WIDTH && pParams->info.clip_info.height == DV100_SYSTEM_1080_HEIGHT) ||
           (pParams->info.clip_info.width == DV100_SYSTEM_720_WIDTH  && pParams->info.clip_info.height == DV100_SYSTEM_720_HEIGHT) ||
           (pParams->info.clip_info.width == 960  && pParams->info.clip_info.height == DV100_SYSTEM_720_HEIGHT) ||
           (pParams->info.clip_info.width == 1280  && pParams->info.clip_info.height == DV100_SYSTEM_1080_HEIGHT)) ||
           (pParams->info.clip_info.width == 1440  && pParams->info.clip_info.height == DV100_SYSTEM_1080_HEIGHT))
        return UMC_ERR_NULL_PTR;

    if( (pParams->info.clip_info.width == DV100_SYSTEM_720_WIDTH ) || ( 960 == pParams->info.clip_info.width))
    {
        //m_VideoFormat = mSystem720_60p;
        m_nSystem720_LastDecodedChannel = 0;
        if ((59.94 == pParams->info.framerate) || (60 == pParams->info.framerate) )
            m_VideoFormat = mSystem720_60p;
        else if (50 == pParams->info.framerate )
            m_VideoFormat = mSystem720_50p;
        else
            return UMC_ERR_INVALID_PARAMS;
    }
    else
    {
        if(pParams->info.framerate == SYSTEM_1080_60I_FRAMERATE)
            m_VideoFormat = mSystem1080_60i;
        else if(pParams->info.framerate == SYSTEM_1080_50I_FRAMERATE)
            m_VideoFormat = mSystem1080_50i;
        else
            return UMC_ERR_INVALID_PARAMS;
    }

    if(UMC_OK != BaseCodec::Init(pBaseParams))
        return UMC_ERR_INIT;

    m_Params = *pParams;
    m_Params.info.stream_type = DIGITAL_VIDEO_HD;

    m_iMaxI = ((mSystem1080_50i == m_VideoFormat) || (mSystem720_50p == m_VideoFormat)) ? 12 : 10;

    // create DV segment writer
    m_pDVWriter = new DVSegmentWriter;
    if (NULL == m_pDVWriter)
        return UMC_ERR_ALLOC ;

    if (m_pDVWriter->InitWriter( (m_VideoFormat == mSystem1080_50i) ? 1 : 0, 0) )
        return UMC_ERR_INIT;

    // create segment compressor
    m_pSCompressor = new DV100SegmentCompressor;
    if (NULL == m_pSCompressor)
        return UMC_ERR_ALLOC ;
    if (m_pSCompressor->InitCompressor(8, m_pMemoryAllocator))
        return UMC_ERR_INIT;

    switch(m_VideoFormat)
    {
        case mSystem1080_60i:
        {
            m_pSReader = new DV100_1080_60i_SegmentReader();
            m_nDownsampledFrameWidth = DV100_SYSTEM_1080_60I_WIDTH_DOWNSAMPLED;
            m_nEncodedFrameSize = 480000;
            m_FrameTimeLength = 2.0/59.96;
        }break;

        case mSystem1080_50i:
        {
            m_pSReader = new DV100_1080_50i_SegmentReader();
            m_nDownsampledFrameWidth = DV100_SYSTEM_1080_50I_WIDTH_DOWNSAMPLED;
            m_nEncodedFrameSize = 576000;
            m_FrameTimeLength = 1.0/25;
        }break;

        case mSystem720_60p:
        {
            m_pSReader = new DV100_720_60p_SegmentReader();
            m_nDownsampledFrameWidth = DV100_SYSTEM_720_60P_WIDTH_DOWNSAMPLED;
            m_nEncodedFrameSize = 240000;
            m_FrameTimeLength = 1.0/59.96;
        }break;

        case mSystem720_50p:
        {
            m_pSReader = new DV100_720_60p_SegmentReader();
            m_nDownsampledFrameWidth = DV100_SYSTEM_720_60P_WIDTH_DOWNSAMPLED;
            m_nEncodedFrameSize = 288000;
            m_FrameTimeLength = 1.0/50;
        }break;
    }
    ((DV100SegmentCompressor*)m_pSCompressor)->m_VideoFormat = m_VideoFormat;

    if (NULL == m_pSReader)
        return UMC_ERR_INIT;

    LumaDataSize = m_nDownsampledFrameWidth * pParams->info.clip_info.height;
    if(UMC_OK != m_pMemoryAllocator->Alloc(&m_DownsampledFrameBufferMID, LumaDataSize*2, UMC_ALLOC_PERSISTENT))
        return UMC_ERR_ALLOC;

    m_DownsampledFrame.Init(m_nDownsampledFrameWidth, pParams->info.clip_info.height, YUV422);

    m_PostProcessing = createVideoProcessing();

    m_bIsInit = true;

    return UMC_OK;
}

Status DV100VideoEncoder::Close()
{
    Status Res = DVVideoEncoder::Close();
    if (Res != UMC_OK)
        return Res;

    if(m_DownsampledFrameBufferMID)
        m_pMemoryAllocator->DeallocateMem(m_DownsampledFrameBufferMID);
    m_DownsampledFrameBufferMID = 0;

    m_nSystem720_LastDecodedChannel = 0;

    m_PostProcessing->Close();
    m_PostProcessing = NULL;

    BaseCodec::Close();

    return UMC_OK;
}


Status DV100VideoEncoder::GetFrame(MediaData *pVideoDataIn, MediaData *pEncodedFrame)
{
    Ipp8u Channel, DIFSeqNum, nChannelsInFrame, DIFSeqAbsOrder;
    Ipp16s VSegmentNum;
    DV_SEGMENT *lpDVSegment;
    V_SEGMENT *lpVSegment;
    Ipp32s LumaDataSize;

    if (!m_bIsInit)
        return UMC_ERR_NOT_INITIALIZED;

    VideoData* pSrcFrame = DynamicCast<VideoData> (pVideoDataIn);
    if (!pSrcFrame || !pEncodedFrame)
        return UMC_ERR_NOT_ENOUGH_DATA;

    if( pEncodedFrame->GetBufferSize() < m_nEncodedFrameSize )
        return UMC_ERR_NOT_ENOUGH_BUFFER;

    if ((m_VideoFormat == mSystem720_60p)|| (m_VideoFormat == mSystem720_50p))
        nChannelsInFrame = 2;
    else
        nChannelsInFrame = 4;

    m_pDVWriter->m_pDest = (Ipp8u *) pEncodedFrame->GetDataPointer();

    LumaDataSize = m_nDownsampledFrameWidth * m_Params.info.clip_info.height;
    Ipp8u *pDownsampledFrameBuffer = (Ipp8u*)m_pMemoryAllocator->Lock(m_DownsampledFrameBufferMID);
    m_DownsampledFrame.SetPlanePointer(pDownsampledFrameBuffer, 0);
    m_DownsampledFrame.SetPlanePointer(pDownsampledFrameBuffer+ LumaDataSize, 1);
    m_DownsampledFrame.SetPlanePointer(pDownsampledFrameBuffer + LumaDataSize + LumaDataSize/2, 2);
    m_DownsampledFrame.SetPlanePitch(m_nDownsampledFrameWidth, 0);
    m_DownsampledFrame.SetPlanePitch(m_nDownsampledFrameWidth/2, 1);
    m_DownsampledFrame.SetPlanePitch(m_nDownsampledFrameWidth/2, 2);

    //m_VideoResizing.GetFrame(pSrcFrame, &m_DownsampledFrame);
    m_PostProcessing->GetFrame(pSrcFrame, &m_DownsampledFrame);

    m_pSReader->m_lpSourceFrame = &m_DownsampledFrame;

    m_pSCompressor->LockWorkBuffers();

    lpVSegment = &(m_pSCompressor->m_VSegment);
    for(Channel=0; Channel<nChannelsInFrame; Channel++)
    {
        for (DIFSeqNum = 0; DIFSeqNum < m_iMaxI; DIFSeqNum++)
        {
            if( (m_VideoFormat == mSystem1080_50i) && (Channel > 0) && (DIFSeqNum == 11))
            {
                //Skip 12-th DIF sequence in 2, 3 and 4 channels
                m_pDVWriter->m_pDest += 12000;
                continue;
            }
            if( (m_VideoFormat == mSystem720_50p) && ((DIFSeqNum == 10) || (DIFSeqNum == 11)) )
            {
                //Skip 10, 11-th DIF sequence in channel
                m_pDVWriter->m_pDest += 12000;
                continue;
            }

            Ipp8u byte_STYPE = 0;
            Ipp8u FSC = Channel%2;
            Ipp8u FSP = 1 - (Channel/2);
            if (mSystem1080_50i == m_VideoFormat)
                byte_STYPE = 0xf4;
            else if (mSystem1080_60i == m_VideoFormat)
                byte_STYPE = 0xD4;
            else if (mSystem720_50p == m_VideoFormat)
                byte_STYPE = 0xf8;
            else if (mSystem720_60p == m_VideoFormat)
                byte_STYPE = 0xD8;

            m_pDVWriter->StartDIFSequence(DIFSeqNum,  FSC, FSP);

            m_pDVWriter->m_pDest -= sizeof(DV_BLOCK);
            m_pDVWriter->m_pDest[48] = 0x60;
            m_pDVWriter->m_pDest[48 + 3] = byte_STYPE;
            m_pDVWriter->m_pDest += sizeof(DV_BLOCK);

            if ((m_VideoFormat == mSystem720_60p)|| (m_VideoFormat == mSystem720_50p))
                //DIFSeqAbsOrder = (Ipp8u)( DIFSeqNum + m_iMaxI*(Channel+m_nSystem720_LastDecodedChannel) );
                DIFSeqAbsOrder = (Ipp8u)( DIFSeqNum + 10*(Channel+m_nSystem720_LastDecodedChannel) );
            else
                DIFSeqAbsOrder = (Ipp8u)( DIFSeqNum + m_iMaxI*Channel );

            for (VSegmentNum = 0; VSegmentNum < 27; VSegmentNum++)
            {
                m_pDVWriter->SetDVSegment(&lpDVSegment);
                m_pSReader->ReadSegment(lpVSegment, DIFSeqAbsOrder, VSegmentNum);
                m_pSCompressor->CompressSegment(lpDVSegment);
            }
        }
    }
    m_pSCompressor->UnlockWorkBuffers();

    m_pMemoryAllocator->Unlock(m_DownsampledFrameBufferMID);

    if ((m_VideoFormat == mSystem720_60p)|| (m_VideoFormat == mSystem720_50p))
    {
        if(m_nSystem720_LastDecodedChannel == 0)
            m_nSystem720_LastDecodedChannel = 2;
        else
            m_nSystem720_LastDecodedChannel = 0;
    }

    pEncodedFrame->SetDataSize( m_nEncodedFrameSize );
    pEncodedFrame->SetTime(pSrcFrame->GetTime(), pSrcFrame->GetTime() + m_FrameTimeLength);

    return UMC_OK;
}

}//namespace UMC

#endif // (UMC_ENABLE_DVHD_VIDEO_ENCODER)
